#include "dbusmethodmodel.hpp"
#include "localmethod.hpp"
QDomDocument DbusMethodModel::introspect(const QString &bname,const QString &bservice,const QString &bPath,const QString &bInterface)
{
    QDomDocument doc;
    QDBusConnection c= getBus(bname);
    QDBusInterface iface(bservice, bPath, QLatin1String("org.freedesktop.DBus.Introspectable"), c);
    if (!iface.isValid()) {
        QDBusError err(iface.lastError());
        emit busError(QString::fromLatin1("Cannot introspect object %1 at %2:\n  %3 (%4)\n").arg(bname).arg(
                          bservice).arg(err.name()).arg(err.message()));
        return doc;
    }

    QDBusReply<QString> xml = iface.call(QLatin1String("Introspect"));

    if (!xml.isValid()) {
        QDBusError err(xml.error());
        if (err.isValid()) {
            emit busError(QString::fromLatin1("Call to object %1 at %2:\n  %3 (%4) failed\n").arg(
                              bPath).arg(bservice).arg(err.name()).arg(err.message()));
        } else {
            emit busError(QString::fromLatin1("Invalid XML received from object %1 at %2\n").arg(
                              bPath).arg(bservice));
        }
        return doc;
    }

    doc.setContent(xml);
    return doc;
}

QDBusConnection DbusMethodModel::getBus(QString name)
{
    if(name == DBUS_NAME_SESSION)
    {
        return QDBusConnection::connectToBus (QDBusConnection::SessionBus,DBUS_NAME_SESSION);
    }else if(name == DBUS_NAME_SYSTEM)
    {
        return QDBusConnection::connectToBus (QDBusConnection::SessionBus,DBUS_NAME_SYSTEM);
    }else
    {
        return QDBusConnection::connectToBus (name,name);
    }
}

void DbusMethodModel::addMethods(DbusMethodItem *parent, const QDomElement &iface)
{
    Q_ASSERT(parent);

    QDomElement child = iface.firstChildElement();
    while (!child.isNull()) {
        DbusMethodItem *item = 0;
        if (child.tagName() == QLatin1String("method")) {
            BusSignatureEx Signature;
            Signature.type = MethodItem;
            Signature.mName = child.attribute(QLatin1String("name"));
            //get "type" from <arg> where "direction" is "in"
            QDomElement n = child.firstChildElement();
            while (!n.isNull()) {
                if (n.attribute(QLatin1String("direction")) == QLatin1String("in"))
                {
                    Signature.mTypeSig += n.attribute(QLatin1String("type"));
                    Signature.argTypes<<n.attribute(QLatin1String("type"));
                    Signature.argNames<<n.attribute(QLatin1String("name"));
                }
                n = n.nextSiblingElement();
            }
            item = new DbusMethodItem(Signature, parent);
        } else if (child.tagName() == QLatin1String("signal")) {
            BusSignatureEx Signature;
            Signature.type = SignalItem;
            Signature.mName = child.attribute(QLatin1String("name"));
            QDomElement n = child.firstChildElement();
            while (!n.isNull()) {
                if (n.attribute(QLatin1String("direction")) == QLatin1String("out"))
                {
                    Signature.mTypeSig += n.attribute(QLatin1String("type"));
                    Signature.argTypes<<n.attribute(QLatin1String("type"));
                    Signature.argNames<<n.attribute(QLatin1String("name"));
                }
                n = n.nextSiblingElement();
            }
            item = new DbusMethodItem(Signature, parent);
        } else if (child.tagName() == QLatin1String("property")) {
            BusSignatureEx Signature;
            QString access = child.attribute(QLatin1String("access"));
            if( access == "read")
            {
                Signature.type = PropertyItemRead;
            }else if(access == "readwrite")
            {
                Signature.type = PropertyItemReadwrite;
            }else
            {
                Signature.type = PropertyItemWrite;
            }

            Signature.mName = child.attribute(QLatin1String("name"));
            Signature.argTypes << child.attribute(QLatin1String("type"));
            Signature.argNames << "args";
            item = new DbusMethodItem(Signature, parent);
        } else {
//            qDebug() << "addMethods: unknown tag:" << child.tagName();
        }
        if (item)
        {
            item->m_data.mBus = parent->m_data.mBus;
            item->m_data.mService = parent->m_data.mService;
            item->m_data.mPath = parent->m_data.mPath;
            item->m_data.mInterface = parent->m_data.mInterface;

            parent->children.append(item);
        }

        child = child.nextSiblingElement();
    }
}

void DbusMethodModel::refresh(const QString &bname,const QString &bservice,const QString &bPath,const QString &bInterface)
{

    QDomDocument doc = introspect(bname,bservice,bPath,bInterface);
    QDomElement node = doc.documentElement();
    QDomElement child = node.firstChildElement();
    beginResetModel ();
    root->m_data.mBus = bname;
    root->m_data.mService = bservice;
    root->m_data.mPath = bPath;
    root->m_data.mInterface = bInterface;
    qDeleteAll(root->children);
    root->children.clear ();
    while (!child.isNull()) {
        if (child.tagName() == QLatin1String("interface"))
        {
            if(child.attribute ("name","") == bInterface)
            {
                addMethods(root, child);
            }

        } else {
//            qDebug() << "addPath: Unknown tag name:" << child.tagName();
        }
        child = child.nextSiblingElement();
    }
    endResetModel ();
}

void DbusMethodModel::refreshlocalMethod()
{
     beginResetModel ();
     root->m_data.type = LocalMethod;
     root->m_data.mBus = "local methods";;
     qDeleteAll(root->children);
     root->children.clear ();

    QStringList list = localObj().methods ();
    for (int i = 0; i < list.count (); ++i) {

            BusSignatureEx Signature;
            Signature.type = LocalMethod;
            Signature.mName = list.at (i);
            Signature.argNames << localObj().getParamNames (list.at (i));
            Signature.argTypes << localObj().getParamDBusTypes (list.at (i));
            DbusMethodItem *item = new DbusMethodItem(Signature, root);
            root->children.append (item);
    }
    endResetModel ();

}


DbusMethodModel::DbusMethodModel(QObject *parent) : QAbstractItemModel(parent)
{
    root=new DbusMethodItem();
}

DbusMethodItem *DbusMethodModel::getItem(const QModelIndex &index)
{
    DbusMethodItem *item = static_cast<DbusMethodItem *>(index.internalPointer());
    return item;
}

QModelIndex DbusMethodModel::index(int row, int column, const QModelIndex &parent) const
{
    return createIndex(row, column, root->children.at(row));
}

QModelIndex DbusMethodModel::parent(const QModelIndex &child) const
{
    DbusMethodItem *item = static_cast<DbusMethodItem *>(child.internalPointer());
    if (!item || !item->parent || !item->parent->parent)
        return QModelIndex();

    return createIndex(item->parent->parent->children.indexOf(item->parent), 0, item->parent);
}

int DbusMethodModel::rowCount(const QModelIndex &parent) const
{
    return root->children.count ();
}

int DbusMethodModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}

QVariant DbusMethodModel::data(const QModelIndex &index, int role) const
{
    const DbusMethodItem *item = static_cast<DbusMethodItem *>(index.internalPointer());
    if (!item)
        return QVariant();
    if (role != Qt::DisplayRole)
        return QVariant();
    if(item->m_data.type == MethodItem)
    {
        return QLatin1String("method: ")+item->m_data.mName;
    }else if(item->m_data.type == SignalItem)
    {
        return QLatin1String("Signal: ")+item->m_data.mName;
    }else if(item->m_data.type == PropertyItemReadwrite || item->m_data.type == PropertyItemRead || item->m_data.type == PropertyItemWrite)
    {
        return QLatin1String("Property: ")+item->m_data.mName;
    }
    return item->m_data.mName;
}

Qt::ItemFlags DbusMethodModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);

    if (index.isValid())
        return Qt::ItemIsDragEnabled | defaultFlags;
    else
        return Qt::ItemIsDragEnabled | defaultFlags;
}

QMimeData *DbusMethodModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);
    QList<BusSignatureEx> rows;
    foreach (const QModelIndex &index, indexes) {
        if (index.isValid()) {
            DbusMethodItem *item = static_cast<DbusMethodItem *>(index.internalPointer());
            if(item)
            {
                rows<<item->m_data;
            }
        }
    }
    mimeData->setImageData (QVariant::fromValue(rows));
    return mimeData;
}

Qt::DropActions DbusMethodModel::supportedDragActions() const
{
//    qDebug()<<1111;
    return Qt::CopyAction;
}

QVariant DbusMethodModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole)
        return "Method";
    return QVariant();
}
